package games.catchTheCreature;



import java.util.ArrayList;
import java.util.Random;



public class CTCState implements Comparable<CTCState>{
	
	private CTCBoard _world;
	private Random _generator;
	private boolean _isWin;
	private int _depth;
	private Heuristic _h;

	private CTCState _father;
	
	public void setHeuristic(Heuristic h){
		_h = h;
	}
	public void setFather(CTCState father){
		_father = father;
	}
	
	public CTCState(CTCState other){
		this(other._world,other._isWin,other._depth,other._h);
		setFather(other);
		_depth++;
	}
	
	public CTCState(String hash,int width,int height){
		_world = new CTCBoard(width,height);
		_generator = new Random();
		_isWin = false;
		_depth = 0;
		for(int x = 0;x<width;x++){
			for(int y=0;y<height;y++){
				char ch = hash.charAt(y*width+x);
				switch(ch){
				case ' ':_world.set(x, height-1-y, CTCSquare.EMPTY);break;
				case 'P':_world.set(x, height-1-y, CTCSquare.PLAYER);
				_world.setPlayerPoint(new Point(x,height-1-y));break;
				case 'C':_world.set(x, height-1-y, CTCSquare.CREATURE);
				_world.setCreaturePoint(new Point(x,height-1-y));break;
				case '#':_world.set(x, height-1-y, CTCSquare.BLOCK);break;
				}
			}
		}
		_father = this;
	}
	
	
	public void printSolutionTrace(){
		CTCState cur = this;
		
		while (cur!=cur._father){
			cur.printState();
			System.out.println();
			cur = cur._father;
		}
		cur.printState();
	}

	public CTCState(CTCBoard b,boolean isWin,int depth,Heuristic h){
		_world = new CTCBoard(b);
		_generator = new Random();
		_isWin = isWin;
		_depth = depth;
		_h = h;
		_father = this;
	}

	public void movePlayer(int x,int y){
		_world.set(_world.getPlayerPoint(), CTCSquare.EMPTY);
		_world.set(x, y, CTCSquare.PLAYER);
		_world.setPlayerPoint(new Point(x,y));
	}

	public void moveCreature(){
		int x,y,d = _generator.nextInt(4);
		int counter = 0;
		do{
			x = _world.getCreaturePoint().getX();
			y = _world.getCreaturePoint().getY();
			if (counter==5)return;
			switch (d){
			case 0: y++; break;
			case 1: y--; break;
			case 2: x++; break;
			case 3: x--; break;
			}
			d = (d + 1)%4;
			counter++;
		}while(!(_world.canStepOn(x, y) && _world.get(x, y)!=CTCSquare.PLAYER));
		
		_world.set(_world.getCreaturePoint(), CTCSquare.EMPTY);
		_world.set(x, y, CTCSquare.CREATURE);
		_world.setCreaturePoint(new Point(x,y));
	}
	
	public CTCState makeAMove(Moves dir){
		int x=_world.getPlayerPoint().getX(),y=_world.getPlayerPoint().getY();
		switch (dir){
		case UP: y++;break;
		case DOWN: y--;break;
		case LEFT: x--;break;
		case RIGHT: x++;break;
		}
		
		if (!_world.canStepOn(x, y)){
			return null;
		}else{
			CTCState s = new CTCState(this);
			if (x == _world.getCreaturePoint().getX() && y == _world.getCreaturePoint().getY()){
				s._isWin = true;
				s.movePlayer(x, y);
				return s;
			}
			s.movePlayer(x, y);
			s.moveCreature();
			return s;
		}
	}
	
	public ArrayList<CTCState> getPossibleMoves(){
		ArrayList<CTCState> res = new ArrayList<CTCState>();
		CTCState tmp = makeAMove(Moves.UP);
		if (tmp!=null) {
			res.add(tmp);
		}
		
		tmp = makeAMove(Moves.DOWN);
		if (tmp!=null) {
			res.add(tmp);
		}
		tmp = makeAMove(Moves.LEFT);
		if (tmp!=null){
			res.add(tmp);
		}
		tmp = makeAMove(Moves.RIGHT);
		if (tmp!=null) {
			res.add(tmp);
		}
		return res;
	}
	
	public boolean isWin(){
		return _isWin;
	}
	
	public int getDepth(){
		return _depth;
	}
	
	public void printState(){
		_world.printWorld();
		System.out.println("Rank: " + _h.getRank(this));//  _h.getDistance(new Point(_world.getCreaturePoint()), new Point(_world.getPlayerPoint())));
		
	}

	
	
	public String hash(){
		String st = "";
		for(int x = 0;x<_world.getWidth();x++){
			for(int y=0;y<_world.getHeight();y++){
				switch (_world.get(x, y)){
				case BLOCK: st+="#";break;
				case CREATURE: st+="C";break;
				case EMPTY: st+=" ";break;
				case PLAYER: st+="P";break;
				}
			}
		}
		return st;
	}

	
	public int compareTo(CTCState o) {
		return _h.getRank(this).compareTo(_h.getRank(o));
	}
	
	
	public CTCState getFather() {
		return _father;
	}
	
	public CTCBoard getWorld(){
		return _world;
	}


}
